package org.amse.korovkin.ciphering.model.cipher.splinewavelet;

import java.util.zip.DataFormatException;
import org.amse.korovkin.ciphering.model.cipher.util.MathUtil;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.amse.korovkin.ciphering.model.cipher.BlockCipher;
import org.amse.korovkin.ciphering.model.cipher.IKey;
import org.amse.korovkin.ciphering.model.cipher.util.TextWork;

/**
 *
 * @author Mikhail Korovkin
 */
public class Spline1WaveletCipher implements BlockCipher{
    private int BLOCK_SIZE = 128;
    private final int N = 257;
    private final String name = "Spline1Wavelet";
    private static BlockCipher cipher = null;

    private Spline1WaveletCipher() {
    }
    public static BlockCipher getInstance() {
        if (cipher == null) {
            cipher = new Spline1WaveletCipher();
        }
        return cipher;
    }

    public String getName() {
        return this.name;
    }

    public int getBlockSize() {
        return BLOCK_SIZE;
    }

    public void setBlockSize(int blockSize) {
        this.BLOCK_SIZE = blockSize;
    }

    public void encrypt(String inputFile, String outputFile, String keyFile)
            throws FileNotFoundException, IOException{
        SplineKey key = new SplineKey();
        inputFile = TextWork.cutComma(inputFile);
        outputFile = TextWork.cutComma(outputFile);
        key.readFromFile(keyFile);
        encrypt(inputFile, outputFile, key);
    }

    public void encrypt(String inputFile, String outputFile, IKey key)
            throws FileNotFoundException, IOException{
        SplineKey spKey = (SplineKey) key;
        List<Integer> plainText = TextWork.readText(inputFile);
        List<Integer> encryptedText = new ArrayList<Integer>();
        List<List<Integer>> blocks = TextWork.getBlocks(plainText, BLOCK_SIZE);
        int K = spKey.getRounds();
        for (int j = 0; j < blocks.size(); j++) {
            SplineKey k = spKey.clone();
            List<Integer> wavelet = new ArrayList<Integer>();
            List<Integer> block = blocks.get(j);
            for (int i = 0; i < K; i++) {

                int num = k.nextNumber();
                int e = k.removeElem(num);

                int cCur = block.remove(num % block.size());
                int cNext = block.get(num % block.size());
                int cPrev = block.get(MathUtil.modul((num - 1), block.size()));

                int xCur = k.getElem(num);
                int xNext = k.getElem(num + 1);
                try {
                    int div = MathUtil.getInverse((xNext - e), N);
                    //System.out.println(cCur + " - " + (xNext-xCur)*div*(cPrev) + " - " + (xCur-e)*div*(cNext));
                    int B =(((cCur) - (xNext-xCur)*div
                            *(cPrev)-(xCur-e)*div*(cNext)) % 256);
                    int b = MathUtil.modul(B, 256);
                    wavelet.add(b);
                } catch (IllegalArgumentException ex) {
                    System.out.println("Error" + ex.getMessage());
                }
                if (i != K - 1) {
                    block = MathUtil.shiftRight(block);
                }
            }
            block.addAll(wavelet);
            encryptedText.addAll(block);
        }
        TextWork.writeText(outputFile, encryptedText);
    }

    public void decrypt(String inputFile, String outputFile, String keyFile)
            throws FileNotFoundException, IOException{
        SplineKey key = new SplineKey();
        inputFile = TextWork.cutComma(inputFile);
        outputFile = TextWork.cutComma(outputFile);
        key.readFromFile(keyFile);
        decrypt(inputFile, outputFile, key);
    }
    public void decrypt(String inputFile, String outputFile, IKey key)
            throws FileNotFoundException, IOException {
        SplineKey spKey = (SplineKey)key;
        List<Integer> encrText = TextWork.readText(inputFile);
        List<List<Integer>> blocks = TextWork.getBlocks(encrText, BLOCK_SIZE);
        List<Integer> decryptedText = new ArrayList<Integer>();
        int K = spKey.getRounds();
        for (int j = 0; j < blocks.size(); j++) {
            SplineKey k = spKey.clone();
            List<Integer> block = blocks.get(j);
            List<Integer> wavelet = new ArrayList<Integer>();
            List<Integer> numbers = new ArrayList<Integer>();
            List<Integer> striking = new ArrayList<Integer>();

            int B = block.size();
            for (int i = 0; i < K; i++) {
                wavelet.add(block.remove(B-K));
                numbers.add(k.nextNumber());
                striking.add(k.removeElem(numbers.get(numbers.size()-1)));
            }
            for (int i = 0; i < K; i++) {
                int num = numbers.get(K-i-1);
                int e = striking.get(K-i-1);

                int xNext = k.getElem(num + 1);
                int xCur = k.getElem(num);

                int cPrev = block.get(MathUtil.modul((num - 1), block.size()));
                int cCur = block.get(num % block.size());

                int b = wavelet.get(K-i-1);
                try {
                    int div = MathUtil.getInverse((xNext - e), N);
                    //System.out.println((xNext-xCur)*div*(cPrev) + " + " + (xCur-e)*div*(cCur) + " + " + b);
                    int C = (((xNext-xCur)*div*(cPrev)
                            +(xCur-e)*div*(cCur) + b) % 256);
                    int c = MathUtil.modul(C, 256);
                    block.add(num % (block.size()+1),c);
                } catch (IllegalArgumentException ex) {
                    System.out.println("Error");
                }
                if (i != K-1) {
                    block = MathUtil.shiftLeft(block);
                }
                k.addElem(num, e);
            }
            decryptedText.addAll(block);
        }
        TextWork.writeText(outputFile, decryptedText);
    }

    public void attack(String inFile, String outFile)
            throws IOException, DataFormatException, InterruptedException {
        throw new UnsupportedOperationException("Not supported yet.");
    }
}
